/**
 *  Serponix is an arcade game in focus to multiplayer based on the classic game Snake.
 *  Copyright (C) 2010 - 2011  Daniel Vala
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License,
 *  or  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.

 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 *  If you have any question do not hesitate to contact author
 *  on e-mail address: danielvala42@gmail.com
 */
package com.serponix.gui;

import com.serponix.game.score.HighSkore;
import com.serponix.settings.SettingsFactory;
import com.serponix.game.*;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import com.serponix.net.Client;
import com.serponix.net.UDPClient;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.io.IOException;
import java.net.InetAddress;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.Box;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import com.serponix.net.Server;

/**
 * Hlavní menu hry obsahuje tlačítka pro založení nové hry, připojení se ke hře,
 * spuštění record módu, zobrazení high-skore, vytvoření mapy, nastavení hry,
 * nápovědy a konec hry.
 * @author Daniel Vala
 */
public class Menu extends JPanel {

    public final String HEADLINE = "Serponix";
    public final String COPYRIGHT = "Serponix Copyright (C) 2011  Daniel Vála www.serponix.com";
    public final String VERSION = "1.1";
    private JButton newGame, join, searchGames, tryRecord, showScore, createMap, settings, help, about, exit;
    private JPanel buttonPanel;
    private Box verticalBox;
    private WindowInterface okno;
    private GameParameters gameParameters;
    private JLabel headlineLabel;
    private Menu menu;

    /**
     * Vytvoří panel hlavního menu.
     * @param okno Okno, ve kterém se menu vytvoří.
     */
    public Menu(WindowInterface okno) {
        menu = this;
        this.okno = okno;
        setLayout(new BorderLayout());

        ////// TVORBA PANELŮ //////
        buttonPanel = new JPanel();
        verticalBox = Box.createVerticalBox();

        ///////////////////////////
        headlineLabel = new JLabel(HEADLINE);
        headlineLabel.setFont(new Font("font1", Font.BOLD, 50));
        headlineLabel.setAlignmentX(0.5f);

        newGame = new JButton("Založit novou hru");
        join = new JButton("Připojit se ke hře");
        searchGames = new JButton("Vyhledat dostupné hry");
        tryRecord = new JButton("Zkusit rekord");
        showScore = new JButton("Ukázat high-skore");
        createMap = new JButton("Vytvořit mapu");
        settings = new JButton("Nastavení hry");
        help = new JButton("Nápověda");
        about = new JButton("O hře");
        exit = new JButton("Konec hry");

        JPanel paticka = new JPanel(new BorderLayout());
        paticka.add(new JLabel(COPYRIGHT), BorderLayout.WEST);
        paticka.add(new JLabel("verze: " + VERSION), BorderLayout.EAST);


        // nastavení velikosti všech tlačítek
        newGame.setMinimumSize(new Dimension(170, 26));
        newGame.setMaximumSize(new Dimension(170, 26));
        newGame.setAlignmentX(0.5f);

        join.setMinimumSize(new Dimension(170, 26));
        join.setMaximumSize(new Dimension(170, 26));
        join.setAlignmentX(0.5f);

        searchGames.setMinimumSize(new Dimension(170, 26));
        searchGames.setMaximumSize(new Dimension(170, 26));
        searchGames.setAlignmentX(0.5f);

        tryRecord.setMinimumSize(new Dimension(170, 26));
        tryRecord.setMaximumSize(new Dimension(170, 26));
        tryRecord.setAlignmentX(0.5f);

        showScore.setMinimumSize(new Dimension(170, 26));
        showScore.setMaximumSize(new Dimension(170, 26));
        showScore.setAlignmentX(0.5f);

        createMap.setMinimumSize(new Dimension(170, 26));
        createMap.setMaximumSize(new Dimension(170, 26));
        createMap.setAlignmentX(0.5f);

        settings.setMinimumSize(new Dimension(170, 26));
        settings.setMaximumSize(new Dimension(170, 26));
        settings.setAlignmentX(0.5f);

        help.setMinimumSize(new Dimension(170, 26));
        help.setMaximumSize(new Dimension(170, 26));
        help.setAlignmentX(0.5f);

        about.setMinimumSize(new Dimension(170, 26));
        about.setMaximumSize(new Dimension(170, 26));
        about.setAlignmentX(0.5f);

        exit.setMinimumSize(new Dimension(170, 26));
        exit.setMaximumSize(new Dimension(170, 26));
        exit.setAlignmentX(0.5f);

        verticalBox.add(headlineLabel);
        verticalBox.add(newGame);
        verticalBox.add(join);
        //   verticalBox.add(vyhledatHry);
        verticalBox.add(tryRecord);
        verticalBox.add(showScore);
        verticalBox.add(createMap);
        verticalBox.add(settings);
        verticalBox.add(help);
        verticalBox.add(about);
        verticalBox.add(exit);


        String[] typy = new String[23];

        // útočné typy
        typy[0] = "Pokud chcete protivníka co nejrychleji zkrátit, bývá nejlepší taktikou „pověsit“ se mu za ocas a střílet. <br /> "
                + "Vzhledem k tomu, že střela hned zasáhne cíl, máte k dispozici ihned plný zásobník.";
        typy[1] = "Dvakrát měř, jednou řež. I zde platí toto přísloví. Pokud dobře zamíříte a vystřelíte, až když jste si jisti zásahem, budete mít k dispozici další střelu dříve. <br /> "
                + "Pokud poprvé minete, pravděpodobně už další šanci mít nebudete a protivník zmizí z dosahu.";
        typy[2] = "Pokud jste při duelu výrazně větší než protivník a vlastníte tank, využívejte ho co nejvíce.";
        typy[3] = "Pokud chcete protivníka zabít na jednu ránu, jeďte přímo proti němu a vystřelte mu do hlavy raketu. <br />";
        typy[4] = "Jestliže se Vám podaří získat vzácnou vlastnost duch, vjeďte do nepřátelského hada a střílejte. Zásobníky budete mít stále plné. <br />"
                + " Pokud takto střelíte raketu, výrazně ho tím poškodíte.";

        // obranné typy
        typy[5] = "Když ocas nepřátelského hada bliká, blíží se k vítězství. Proto než sežere další jídlo, pokuste se mu ubrat alespoň 3 články. <br />"
                + "Právě tolik je totiž standardní růst hada po sežrání jídla.";
        typy[6] = "Nepouštějte se do souboje na život a na smrt s delším hadem, pokud si opravdu nevěříte.";
        typy[7] = "Pokud se budete vlnit, jako se správný had vlnit má, snížíte tak šanci, že utržíte ránu. <br />"
                + " Nepřátelský had se netrefí tak snadno, a jakmile mine, potrvá, než se mu zásobník opět nabije.";
        typy[8] = "Pokud vlastníte tank, snažte se sežrat jídlo i za cenu využití tanku k resetování jiného hada. Sice tím přijdete o jeden článek, <br />"
                + "stejně tak ale o něj přijde i soupeř. Vy navíc sežráním jídla získáte minimálně 3 další články.";
        typy[9] = "Pokud jste příliš malí, zaměřte se raději na sebírání jídla, než bonusů. Bonus pro Vás nemusí být vždy přínosem a riskujete, že Vás protihráči zabijí.";
        typy[10] = "Jestliže jste pod nepřátelskou palbou a cítíte, že by Vás nepřítel mohl zabít, raději nabourejte do stěny mapy nebo do sebe. <br />"
                + "Ztratíte tím sice jeden článek, ale většinou se objevíte dál od protivníka.";
        typy[11] = "Pokud se jako dlouhý had zamotáte, bývá někdy výhodné střelit sám sebe a tím se zachránit od nárazu do sebe.";
        typy[12] = "Když už se Váš život chýlí ke konci, vyhněte se všem hadům a někde tiše vyčkávejte na jídlo.";
        typy[13] = "Raketu si šetřete na horší časy.";
        typy[14] = "Většinou bývá lepší se \"starat o sebe\" a sbírat jídlo, než bojovat s ostatními";
        // další typy
        typy[15] = "Zvolte menší rychlost hry pro více strategickou hru. Naopak větší rychlost hry pro více reflexní hru.";
        typy[16] = "Pokud vlastníte lopatu, snažte se ji využít pro co nejkratší cestu za jídlem či bonusem. Díky svobodě pohybu skrz krajní stěny mapy <br />"
                + " získáte drahocenný náskok před protivníkem a tím seberete jídlo nebo bonus jako první.";
        typy[17] = "Při výrazně zvýšeném růstu díky pilulkám se snažte vyhrát tím způsobem, že dosáhnete jako první potřebné velikosti pro vítězství, než zabíjením ostatních hadů.";
        typy[18] = "Při velkém počtu hráčů bývá prvních pár vteřin hry nejtěžších. Věnujte tedy startu hry zvýšenou koncentraci.";
        typy[19] = "Pokud jste duchem, můžete se pohybovat nejen skrz všechny hady, ale i skrz zdi!";
        typy[20] = "Při kreslení mapy berte v úvahu, že se jídlo může objevit kdekoliv. Proto když se objeví na nedostupném místě, <br />"
                + "strhne se hra o boji na život a na smrt bez sbírání jídla.";
        typy[21] = "Hlavou zeď neprorazíš. Toto známé přísloví platí, i když vlastníte tank.";
        typy[22] = "Nesbírejte bonus, pokud už jste dost velcí. Zbytečně riskujete prohození s hráčem, který je \"na umření\" a ten poté vyhraje.";


        Random random = new Random();
        int cisloNahodnehoTypu = random.nextInt(typy.length);

        String typTypu;
        if (cisloNahodnehoTypu <= 4) {
            typTypu = "Útočný typ do hry:";
        } else if (cisloNahodnehoTypu <= 14) {
            typTypu = "Obranný typ do hry:";
        } else {
            typTypu = "Typ do hry:";
        }

        JLabel typ = new JLabel("<html>" + typTypu + "<br /><table CELLSPACING=\"0\" CELLPADDING=\"2\" BORDER=\"1\" BORDERCOLOR=\"BLACK\"><TD BGCOLOR=\"#99CCFF\">" + typy[cisloNahodnehoTypu] + "</TD></TR></table></html>");
        JPanel panelTyp = new JPanel();
        panelTyp.add(typ);


        newGame.addActionListener(new HandlerNovaHra());
        join.addActionListener(new HandlerPripojSe());
        searchGames.addActionListener(new HandlerVyhledejHry());
        tryRecord.addActionListener(new HandlerZkusitRekord());
        showScore.addActionListener(new HandlerUkazSkore());
        createMap.addActionListener(new HandlerVytvorMapu());
        settings.addActionListener(new HandlerNastaveni());
        help.addActionListener(new HandlerNapoveda());
        about.addActionListener(new HandlerOHre());
        exit.addActionListener(new HandlerKonec());

        // key listenery pro pohyb menu skrz směrové klávesy
        newGame.addKeyListener(new HandlerPohyb(exit, join));
        join.addKeyListener(new HandlerPohyb(newGame, tryRecord));
        //   vyhledatHry.addKeyListener(new HandlerPohyb(pripojitSe, zkusitRekord));
        tryRecord.addKeyListener(new HandlerPohyb(join, showScore));
        showScore.addKeyListener(new HandlerPohyb(tryRecord, createMap));
        createMap.addKeyListener(new HandlerPohyb(showScore, settings));
        settings.addKeyListener(new HandlerPohyb(createMap, help));
        help.addKeyListener(new HandlerPohyb(settings, about));
        about.addKeyListener(new HandlerPohyb(help, exit));
        exit.addKeyListener(new HandlerPohyb(about, newGame));

        ///// PŘIDÁNÍ PANELŮ /////
        buttonPanel.add(verticalBox);

        add(buttonPanel, BorderLayout.NORTH);
        add(panelTyp, BorderLayout.CENTER);
        add(paticka, BorderLayout.SOUTH);

    }

    /**
     * Nastaví tlačítko jako vybrané tak, že se bude moci stisknout enterem.
     * @param button Tlačítko, které bude nastaveno jako vybrané.
     */
    public void setButtonAsChosen(JButton button) {
        getRootPane().setDefaultButton(button);
        button.requestFocus();
    }

    /**
     * Po akci zobrazí lobby.
     */
    private class HandlerNovaHra implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            okno.displayLobbyForServer();
        }
    }

    /**
     * Po akci zobrazí nastavení.
     */
    private class HandlerNastaveni implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            okno.zobrazPanelNastaveni();
        }
    }

    /**
     * Po akci zobrazí nápovědu.
     */
    private class HandlerNapoveda implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            okno.zobrazPanelNapovedy();
        }
    }

    /**
     * Po akci zobrazí informace o hře.
     */
    private class HandlerOHre implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            okno.zobrazPanelOHre();
        }
    }

    /**
     * Po akci zobrazí high-skore.
     */
    private class HandlerUkazSkore implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            HighSkore.zobrazSkore();
        }
    }

    /**
     * Po akci spustí mód rekord.
     */
    private class HandlerZkusitRekord implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            List<Player> hraci = new ArrayList<Player>();
            hraci.add(new Player(SettingsFactory.getNastaveniHry().getJmeno(0), Player.Control.KEYBOARD1, null));
            GameParameters parametryHry = new GameParameters(Mode.RECORD, 40, hraci, new Mapa(), null, (Server) null);
            okno.displayGame(parametryHry);
        }
    }

    /**
     * Po akci spustí editor map.
     */
    private class HandlerVytvorMapu implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            gameParameters = new GameParameters(Mode.EDITOR);
            okno.displayGame(gameParameters);
        }
    }

    /**
     * Po akci zobrazí dialog na zadání IP adresy.
     * Zadanou IP adresou se pak pokusí připojit se k serveru.
     */
    private class HandlerPripojSe implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            String serverIP = JOptionPane.showInputDialog("Zadejte IP adresu", SettingsFactory.getNastaveniHry().getServerIP());
            if (serverIP == null) {
                return;
            }
            SettingsFactory.getNastaveniHry().setServerIP(serverIP);
            SettingsFactory.uloz();

            Client client = new UDPClient();
            try {
                boolean uspech = client.pripojitSeKServeru(InetAddress.getByName(serverIP));
                if (!uspech) {
                    JOptionPane.showMessageDialog(menu, "Server je plný", "Neúspěch", JOptionPane.ERROR_MESSAGE);
                    return;
                }
            } catch (SocketTimeoutException ex) {
                JOptionPane.showMessageDialog(menu, "Hra nebyla nalezena \n Vypršel časový limit spojení. \n Ověřte, zda používáte stejný port jako server.", "Chyba", JOptionPane.ERROR_MESSAGE);
                return;
            } catch (UnknownHostException ex) {
                JOptionPane.showMessageDialog(menu, " Hra nebyla nalezena \n IP adresa nebyla rozpoznána. \n Ověřte, zda používáte stejný port jako server.", "Chyba", JOptionPane.ERROR_MESSAGE);
                return;
            } catch (IOException ex) {
                JOptionPane.showMessageDialog(menu, " Hra nebyla nalezena \n Chyba připojení k serveru \n Ověřte, zda používáte stejný port jako server.", "Chyba", JOptionPane.ERROR_MESSAGE);
                return;
            }
            okno.displayLobbyForClient(client);
        }
    }

    /**
     * Po akci vyhledá založené hry a zobrazí okno s přehledem nalezených her,
     * do kterých je možno se připojit.
     */
    private class HandlerVyhledejHry implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            List<InetAddress> nalezeneAdresy = null;
            Client client = new UDPClient();
            try {
                nalezeneAdresy = client.vyhledatHry();
            } catch (IOException ex) {
                System.err.println("Chyba: " + ex.getMessage());
            }
            final List<InetAddress> finalniNalezeneAdresy = nalezeneAdresy;
            final JFrame oknoVyhledanychHer = new JFrame("dostupné hry");
            Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
            Dimension zakladniRozliseni = new Dimension(400, 300);
            oknoVyhledanychHer.setBounds((int) screen.getWidth() / 2 - (int) zakladniRozliseni.getWidth() / 2, 100, (int) zakladniRozliseni.getWidth(), (int) zakladniRozliseni.getHeight());
            oknoVyhledanychHer.setVisible(true);
            JPanel panel = new JPanel();

            if (nalezeneAdresy == null || nalezeneAdresy.isEmpty()) {
                JLabel nenalezeno = new JLabel("žádná hra nebyla nalezena");
                panel.add(nenalezeno);
            }

            ButtonGroup skupinaHer = new ButtonGroup();

            final JRadioButton[] nalezeneHry = new JRadioButton[nalezeneAdresy.size()];
            for (int i = 0; i < nalezeneHry.length; i++) {
                nalezeneHry[i] = new JRadioButton(nalezeneAdresy.get(i).toString());
                skupinaHer.add(nalezeneHry[i]);
                panel.add(nalezeneHry[i]);
            }

            JButton pripojit = new JButton("připojit se");
            pripojit.setSize(getPreferredSize());
            JButton zrusit = new JButton("zrušit");
            zrusit.setSize(getPreferredSize());
            panel.add(pripojit, BorderLayout.SOUTH);
            panel.add(zrusit, BorderLayout.SOUTH);
            oknoVyhledanychHer.add(panel);

            pripojit.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    for (int i = 0; i < nalezeneHry.length; i++) {
                        if (nalezeneHry[i].isSelected()) {
                            Client client = new UDPClient();
                            try {
                                client.pripojitSeKServeru(finalniNalezeneAdresy.get(i));
                            } catch (SocketTimeoutException ex) {
                                JOptionPane.showMessageDialog(null, "Chyba připojení k serveru", "Chyba", JOptionPane.ERROR_MESSAGE);
                                return;
                            } catch (IOException ex) {
                                JOptionPane.showMessageDialog(null, "Chyba připojení k serveru2", "Chyba", JOptionPane.ERROR_MESSAGE);
                                return;
                            }
                            oknoVyhledanychHer.setVisible(false);
                            okno.displayLobbyForClient(client);
                            break;
                        }
                    }
                }
            });

            zrusit.addActionListener(new ActionListener() {

                @Override
                public void actionPerformed(ActionEvent e) {
                    oknoVyhledanychHer.setVisible(false);
                }
            });

        }
    }

    /**
     * Po akci ukončí hru.
     */
    private class HandlerKonec implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    }

    /**
     * Třída slouží pro pohyb po menu směrovými klávesami.
     */
    private class HandlerPohyb extends KeyAdapter {

        JButton tlacitkoUp;
        JButton tlacitkoDown;

        public HandlerPohyb(JButton tlacitkoUp, JButton tlacitkoDown) {
            this.tlacitkoUp = tlacitkoUp;
            this.tlacitkoDown = tlacitkoDown;
        }

        @Override
        public void keyPressed(KeyEvent e) {
            switch (e.getKeyCode()) {
                case KeyEvent.VK_UP:
                    setButtonAsChosen(tlacitkoUp);
                    tlacitkoUp.requestFocus();
                    break;
                case KeyEvent.VK_DOWN:
                    setButtonAsChosen(tlacitkoDown);
                    tlacitkoDown.requestFocus();
                    break;
            }
        }
    }

    /**
     * Set button <b> new game </b> as default.
     * @param g
     */
    @Override
    public void paint(Graphics g) {
        super.paint(g);
        setButtonAsChosen(newGame);
    }
}
